#include "topdbs_connection_pool.hpp"

namespace topdbs
{
    std::mutex  SQLConnectionPool::m_lock; 
    bool SQLConnectionPool::m_status = false;
    SQLConnectionPool* SQLConnectionPool::m_instance = nullptr;
    SQLConnectionPool::AutoRelease SQLConnectionPool::m_release;

    SQLConnectionPool::~SQLConnectionPool()
    {
        //DeleteConnectionPool();
    }

    int SQLConnectionPool::NewConnectionPool(
        int minConn,
        int maxConn,
        eDBSClient_t dbsClient,
        const string& dbString,
        const string& user ,
        const string& pass ,
        const string& charset,
        bool encrypt,
        void* pOption )
    {
        std::lock_guard<std::mutex> _lock(SQLConnectionPool::m_lock);
        if(SQLConnectionPool::m_status)
        {
            return ERR_DBS_OK;
        }	
        
        m_current_pool_size = 0;
        m_current_idle_size = 0;
        
        m_minConn = minConn;
        m_maxConn = maxConn;
        m_dbsClient = dbsClient;
        m_dbString = dbString;
        m_user = user;
        m_pass = pass;
        m_charset = charset;
        m_encrypt = encrypt;
        m_pOption = pOption;
        
        for(int i = 0; i < minConn; i++)
        {
            NewConnection();
        }
        SQLConnectionPool::m_status = true;
        dbslog(DBS_DEBUG,"Create database Connection pool succed. current_pool_size=%d",
            m_current_pool_size);
        return ERR_DBS_OK;
    }

    int SQLConnectionPool::DeleteConnectionPool()
    {
        dbslog(DBS_DEBUG,"begin exec delete connection pool size = %d",m_StConns.size());
        for(auto it = m_StConns.begin(); it != m_StConns.end(); )
        {
            auto& pStConn = it->second;
            dbslog(DBS_DEBUG,"Delete database Connection succed. connectionID=%lu",
                pStConn->m_pConnection->getConnectionID());
            delete pStConn;
            pStConn = nullptr;
            m_StConns.erase(it++);
        }
        m_current_idle_size = 0;
        m_current_pool_size = 0;
        return ERR_DBS_OK;
    }

    int SQLConnectionPool::NewConnection()
    {
        SQLConnection* pConnection = new SQLConnection();
        pConnection->setClientEncode(m_charset);
        pConnection->setEncrypt(m_encrypt);

        if( pConnection->connect(m_dbsClient,m_dbString,m_user,m_pass,m_pOption ) != 0)
        {
            delete pConnection;
            pConnection = nullptr;
            return ERR_DBS_FAIL;
        }
        STConn* pStConn = new STConn();
        pStConn->m_status = ConnSt_Idle;
        pStConn->m_pConnection = pConnection;
        uint64_t nConnectionID = pConnection->getConnectionID();
        m_StConns[nConnectionID] = pStConn;
        m_current_pool_size++;	
        m_current_idle_size++;
        dbslog(DBS_DEBUG,"New database Connection succed. connectionID=%lu",nConnectionID);
        return	ERR_DBS_OK;
    }	

    SQLConnection* SQLConnectionPool::GetIdleConnection()
    {
        SQLConnection* pConnection = nullptr;
        std::lock_guard<std::mutex> _lock(SQLConnectionPool::m_lock);
        if( m_current_idle_size == 0 && m_current_pool_size  < m_maxConn)
        {
            NewConnection();
        }
        else if(m_StConns.size() > 0 &&  m_current_idle_size > m_minConn)
        {
            auto it = m_StConns.begin();
            auto& pStConn = it->second;
            delete pStConn;
            pStConn = nullptr;
            m_StConns.erase(it);
        }
        for(auto it = m_StConns.begin(); it != m_StConns.end(); it++)
        {
            auto& pStConn = it->second;
            if(pStConn->m_status == ConnSt_Idle)
            {
                pConnection = pStConn->m_pConnection;
                pStConn->m_status = ConnSt_Busy;
                m_current_idle_size--;
                return pConnection;
            }	
        }    
        return pConnection;
    };	

    int SQLConnectionPool::ReleaseIdleConnection(SQLConnection* pConnection)
    {
        if(!pConnection)
        {
            dbslog(DBS_ERROR,"The input args invalid!!!");
            return ERR_DBS_FAIL;
        }
        std::lock_guard<std::mutex> _lock(SQLConnectionPool::m_lock);
        
        uint64_t nConnectionID = pConnection->getConnectionID();
        auto it = m_StConns.find(nConnectionID);
        if(it == m_StConns.end())
        {
            dbslog(DBS_ERROR,"Not found connection info!!!");
            return ERR_DBS_FAIL;
        }
        auto& pStConn = it->second;
        pStConn->m_status = ConnSt_Idle;
        m_current_idle_size++;
        return ERR_DBS_OK;
    }	

    int SQLConnectionPool::InitEnv(
        uint16_t minConn,
        uint16_t maxConn,
        eDBSClient_t dbsClient,
        const string& dbString,
        const string& user ,
        const string& pass ,
        const string& charset,
        bool encrypt,
        void* pOption )
    {
        SQLConnectionPool* instance = nullptr;
        try
        {
            instance = SQLConnectionPool::getInstance();
            if(!instance)
            {
                dbslog(DBS_ERROR,"getInstance error!!!");
                return ERR_DBS_FAIL;
            }
        }
        catch(const std::exception& ex)
        {
            dbslog(DBS_ERROR,"getInstance exception %s!!!",ex.what());
            return ERR_DBS_FAIL;
        }
        return instance->NewConnectionPool(minConn,maxConn,dbsClient,dbString,user,pass,charset,encrypt,pOption);
    }	

    int SQLConnectionPool::InitEnv(
        eDBSClient_t dbsClient,
        const string& dbString,
        const string& user ,
        const string& pass ,
        const string& charset,
        bool encrypt,
        void* pOption )
    {
        return SQLConnectionPool::InitEnv(1,5,dbsClient,dbString,user,pass,charset,encrypt,pOption);
    }	

    int SQLConnectionPool::InitEnv( const string& dbsfile,const string& dbsgroup)
    {
        DBSConnectionParams dbsConfig;
        if( readDbsfile(dbsfile,dbsConfig,dbsgroup) != 0)
        {
            return ERR_DBS_FAIL;
        }        
        return SQLConnectionPool::InitEnv(dbsConfig.dbsMinconn,dbsConfig.dbsMaxconn,
            dbsConfig.dbsType,dbsConfig.dbsConnStr,dbsConfig.dbsUser,dbsConfig.dbsPass,dbsConfig.dbsCharset);
    }

    int SQLConnectionPool::UnInitEnv()
    {
        if (SQLConnectionPool::m_instance)
        {
            std::lock_guard<std::mutex> _lock(SQLConnectionPool::m_lock);
            if (SQLConnectionPool::m_instance)
            {
                SQLConnectionPool::m_instance->DeleteConnectionPool();
                delete SQLConnectionPool::m_instance;
                SQLConnectionPool::m_instance = nullptr;
            }
        }
        return ERR_DBS_OK;
    }
        
    SQLConnectionPool* SQLConnectionPool::getInstance()
    {
        if (SQLConnectionPool::m_instance==nullptr)
        {
            std::lock_guard<std::mutex> _lock(SQLConnectionPool::m_lock);
            if(SQLConnectionPool::m_instance==nullptr)
            {
                SQLConnectionPool::m_instance = new SQLConnectionPool();
            }
        }
        return SQLConnectionPool::m_instance;				
    }

    SQLConnection* SQLConnectionPool::GetConnection()
    {
        return SQLConnectionPool::getInstance() 
            ? SQLConnectionPool::getInstance()->GetIdleConnection() 
            : nullptr;
    }

    int SQLConnectionPool::ReleaseConnection(SQLConnection* pConnection)
    {
        return SQLConnectionPool::getInstance() 
            ? SQLConnectionPool::getInstance()->ReleaseIdleConnection(pConnection) 
            : ERR_DBS_FAIL ;
    }
}